package com.uinnova.product.eam.model.cj.utils;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.binary.core.util.BinaryUtils;
import com.uinnova.project.base.diagram.comm.model.*;
import com.uinnova.project.base.diagram.util.FileFilterUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
@Slf4j
public class ZipUtils {
    private static final int BUFFER_SIZE = 2 * 1024;
    private static String domain = "https://monet.quickea.com/";

    /**
     * 压缩成ZIP 方法1
     *
     * @param srcDir           压缩文件夹路径
     * @param out              压缩文件输出流
     * @param KeepDirStructure 是否保留原来的目录结构,true:保留目录结构;
     *                         false:所有文件跑到压缩包根目录下(注意：不保留目录结构可能会出现同名文件,会压缩失败)
     * @throws RuntimeException 压缩失败会抛出运行时异常
     */
    public static void toZip(String srcDir, OutputStream out, boolean KeepDirStructure)
            throws RuntimeException {

        long start = System.currentTimeMillis();
        ZipOutputStream zos = null;
        try {
            zos = new ZipOutputStream(out);
            File sourceFile = new File(srcDir);
            compress(sourceFile, zos, sourceFile.getName(), KeepDirStructure);
            long end = System.currentTimeMillis();
            log.info("压缩完成，耗时：%d ms",(end - start));
        } catch (Exception e) {
            throw new RuntimeException("zip error from ZipUtils", e);
        } finally {
            if (zos != null) {
                try {
                    zos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    /**
     * 解压缩
     *
     * @return
     * @paramsZipPathFile要解压的文件
     * @paramsDestPath解压到某文件夹
     */
    public static void decompress(String sZipPathFile, String sDestPath) {
        try (FileInputStream fins = new FileInputStream(sZipPathFile);
             ZipInputStream ins = new ZipInputStream(fins)){
            /*//先指定压缩档的位置和档名，建立FileInputStream对象
            FileInputStream fins = new FileInputStream(sZipPathFile);
            //将fins传入ZipInputStream中
            ZipInputStream ins = new ZipInputStream(fins);*/
            ZipEntry ze ;
            byte[] ch = new byte[256];
            while ((ze = ins.getNextEntry()) != null) {
                File zfile = new File(FileFilterUtil.parseFilePath(sDestPath + ze.getName()));
                File fpath = new File(zfile.getParentFile().getPath());
                if (ze.isDirectory()) {
                    if (!zfile.exists())
                        zfile.mkdirs();
                    ins.closeEntry();
                } else {
                    try (FileOutputStream fouts = new FileOutputStream(zfile)) {
                        if (!fpath.exists())
                            fpath.mkdirs();
                        int i;
                        while ((i = ins.read(ch)) != -1)
                            fouts.write(ch, 0, i);
                        ins.closeEntry();
                    }catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (Exception e) {
            log.error("decompress:",e.getMessage());
        }
    }


    /**
     * 递归压缩方法
     *
     * @param sourceFile       源文件
     * @param zos              zip输出流
     * @param name             压缩后的名称
     * @param KeepDirStructure 是否保留原来的目录结构,true:保留目录结构;
     *                         false:所有文件跑到压缩包根目录下(注意：不保留目录结构可能会出现同名文件,会压缩失败)
     * @throws Exception
     */
    private static void compress(File sourceFile, ZipOutputStream zos, String name,
                                 boolean KeepDirStructure) throws Exception {
        byte[] buf = new byte[BUFFER_SIZE];
        if (sourceFile.isFile()) {
            try(FileInputStream in = new FileInputStream(sourceFile)) {
                // 向zip输出流中添加一个zip实体，构造器中name为zip实体的文件的名字
                zos.putNextEntry(new ZipEntry(name));
                // copy文件到zip输出流中
                int len;
                while ((len = in.read(buf)) != -1) {
                    zos.write(buf, 0, len);
                }
                // Complete the entry
                zos.closeEntry();
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            File[] listFiles = sourceFile.listFiles();
            if (listFiles == null || listFiles.length == 0) {
                // 需要保留原来的文件结构时,需要对空文件夹进行处理
                if (KeepDirStructure) {
                    // 空文件夹的处理
                    zos.putNextEntry(new ZipEntry(name + "/"));
                    // 没有文件，不需要文件的copy
                    zos.closeEntry();
                }

            } else {
                for (File file : listFiles) {
                    // 判断是否需要保留原来的文件结构
                    if (KeepDirStructure) {
                        // 注意：file.getName()前面需要带上父文件夹的名字加一斜杠,
                        // 不然最后压缩包中就不能保留原来的文件结构,即：所有文件都跑到压缩包根目录下了
                        compress(file, zos, name + "/" + file.getName(), KeepDirStructure);
                    } else {
                        compress(file, zos, file.getName(), KeepDirStructure);
                    }

                }
            }
        }
    }

    // 文件复制
    // Java NIO包括transferFrom方法,根据文档应该比文件流复制的速度更快
    public static void copyFileUsingFileChannels(File source, File dest) throws IOException {
        FileChannel inputChannel = null;
        FileChannel outputChannel = null;
        File parent = dest.getParentFile();
        if (parent != null && !parent.exists() && !parent.mkdirs()) {
            throw new IOException("File '" + dest + "' could not be created");
        }
        try {

            inputChannel = new FileInputStream(source).getChannel();
            outputChannel = new FileOutputStream(dest).getChannel();
            outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputChannel != null) {
                    inputChannel.close();
                }
                if (outputChannel != null) {
                    outputChannel.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void getDiagram(JSONObject jsonObject, Map<String, JSONObject> objects, Set<String> fileList, String rsm) {
        JSONArray jsonArray = jsonObject.getJSONArray("data");
        for (int i = 0; i < jsonArray.size(); i++) {
            ESDiagramDTO esDiagramDTO = jsonArray.get(i, ESDiagramDTO.class);
            ESDiagramInfoDTO diagram = esDiagramDTO.getDiagram();
            List<ESDiagramSheetDTO> sheetList = diagram.getSheetList();
            List<ESDiagramModel> modelList = diagram.getModelList();
            modelList.forEach(esDiagram -> {
                List<ESDiagramNode> nodes = esDiagram.getNodeDataArray();
                List<Object> nodeDataArray = new ArrayList<>();
                JSONObject sheetObject = new JSONObject(esDiagram);
                if (!BinaryUtils.isEmpty(nodes)) {
                    nodes.forEach(node -> {
                        String nodeJson = node.getNodeJson();
                        JSONObject nodeJSONObject = new JSONObject(nodeJson);
                        String image = nodeJSONObject.getStr("image");
                        if (!BinaryUtils.isEmpty(image)) {
                            if (image.startsWith("http")) {
                                image = image.replace(domain, "");
                                fileList.add(image);
                                image = rsm + image.replace(rsm, "");
                                nodeJSONObject.remove("image");
                                nodeJSONObject = nodeJSONObject.putOnce("image", image);
                            } else {
                                fileList.add(image);
                            }
                        }
                        nodeDataArray.add(nodeJSONObject);
                    });
                    sheetObject.remove("nodeDataArray");
                    sheetObject.putOnce("nodeDataArray", nodeDataArray);
                }
                List<ESDiagramLink> links = esDiagram.getLinkDataArray();
                if (!BinaryUtils.isEmpty(links)) {
                    List<Object> linkDataArray = new ArrayList<>();
                    links.forEach(link -> {
                        String nodeJson = link.getLinkJson();
                        JSONObject nodeJSONObject = new JSONObject(nodeJson);
                        linkDataArray.add(nodeJSONObject);
                    });
                    sheetObject.remove("linkDataArray");
                    sheetObject.putOnce("linkDataArray", linkDataArray);
                }
                objects.put(esDiagram.getSheetId(), sheetObject);
            });
            sheetList.forEach(sheet -> {
                if (objects.containsKey(sheet.getSheetId())) {
                    JSONObject object = objects.get(sheet.getSheetId());
                    object.putOnce("id", sheet.getId());
                    object.putOnce("name", sheet.getName());
                    object.putOnce("sheetOrder", sheet.getSheetOrder());
                    object.putOnce("active", sheet.getActive());
                    objects.put(sheet.getSheetId(), object);
                }
            });
        }
    }


    public static void downDiagram(ESDiagramInfoDTO diagram, List<JSONObject> objects, Set<String> fileList, String rsm, String exploreKey) {
        List<ESDiagramModel> modelList = diagram.getModelList();
        String diagramsUrl = "/diagrams/" + exploreKey;
        modelList.forEach(esDiagram -> {
            List<ESDiagramNode> nodes = esDiagram.getNodeDataArray();
            List<Object> nodeDataArray = new ArrayList<>();
            List<Object> linkDataArray = new ArrayList<>();
            JSONObject sheetObject = new JSONObject(esDiagram);
            if (!BinaryUtils.isEmpty(nodes)) {
                nodes.forEach(node -> {
                    String nodeJson = node.getNodeJson();
                    JSONObject nodeJSONObject = new JSONObject(nodeJson);
                    String image = nodeJSONObject.getStr("image");
                    if (!BinaryUtils.isEmpty(image)) {
                        image = image.split("\\?")[0];
//                        image = image.replace("//", "/");
                        fileList.add(image);
                        if (image.startsWith("http")) {
                            try {
                                URL url = new URL(image);
                                image = image.replace(url.getProtocol() + "://" + url.getHost(), "");
                                image = diagramsUrl + rsm + image.replace(rsm, "");
                                nodeJSONObject.remove("image");
                                nodeJSONObject = nodeJSONObject.putOnce("image", image);
                            } catch (MalformedURLException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    nodeDataArray.add(nodeJSONObject);
                });
            }
            sheetObject.remove("nodeDataArray");
            sheetObject.putOnce("nodeDataArray", nodeDataArray);
            List<ESDiagramLink> links = esDiagram.getLinkDataArray();
            if (!BinaryUtils.isEmpty(links)) {
                links.forEach(link -> {
                    String nodeJson = link.getLinkJson();
                    JSONObject nodeJSONObject = new JSONObject(nodeJson);
                    linkDataArray.add(nodeJSONObject);
                });
            }
            sheetObject.remove("linkDataArray");
            sheetObject.putOnce("linkDataArray", linkDataArray);
            sheetObject.remove("diagramId");
            sheetObject.putOnce("diagramId", sheetObject.get("dEnergy"));
            sheetObject.remove("dEnergy");
            objects.add(sheetObject);
        });
    }


    /*public static void downDiagramById(ESDiagramInfoDTO diagram, List<JSONObject> objects, Set<String> fileList, String rsm, String diagramsUrl) {
        List<ESDiagramModel> modelList = diagram.getModelList();
        modelList.forEach(esDiagram -> {
            List<ESDiagramNode> nodes = esDiagram.getNodeDataArray();
            List<Object> nodeDataArray = new ArrayList<>();
            List<Object> linkDataArray = new ArrayList<>();
            JSONObject sheetObject = new JSONObject(esDiagram);
            if (!BinaryUtils.isEmpty(nodes)) {
                nodes.forEach(node -> {
                    String nodeJson = node.getNodeJson();
                    JSONObject nodeJSONObject = new JSONObject(nodeJson);
                    String image = nodeJSONObject.getStr("image");
                    if (!BinaryUtils.isEmpty(image)) {
                        image = image.split("\\?")[0];
                        fileList.add(image);
                        nodeJSONObject.remove("image");
                        nodeJSONObject = nodeJSONObject.putOnce("image", image);

                    }
                    nodeDataArray.add(nodeJSONObject);
                });
            }
            sheetObject.remove("nodeDataArray");
            sheetObject.putOnce("nodeDataArray", nodeDataArray);
            List<ESDiagramLink> links = esDiagram.getLinkDataArray();
            if (!BinaryUtils.isEmpty(links)) {
                links.forEach(link -> {
                    String nodeJson = link.getLinkJson();
                    JSONObject nodeJSONObject = new JSONObject(nodeJson);
                    linkDataArray.add(nodeJSONObject);
                });
            }
            sheetObject.remove("linkDataArray");
            sheetObject.putOnce("linkDataArray", linkDataArray);
            sheetObject.remove("diagramId");
            sheetObject.putOnce("diagramId", sheetObject.get("dEnergy"));
            sheetObject.remove("dEnergy");
            objects.add(sheetObject);
        });
    }*/

    public static void writeDealInfo(String url, String message) {
        try (FileWriter fw = new FileWriter(url, false)) {
            File file = new File(url);
            if (!file.exists()) {
                File parent = file.getParentFile();
                if (parent != null && !parent.exists() && !parent.mkdirs()) {
                    throw new IOException("File '" + file + "' could not be created");
                }
            }
            fw.write(message);
        } catch (Exception e) {
            log.error("书写日志发生错误：",e.toString());
        }
    }

    //删除文件夹
    public static void delFolder(String folderPath) {
        try {
            delAllFile(folderPath); //删除完里面所有内容
            File myFilePath = new File(folderPath);
            boolean delete = myFilePath.delete();//删除空文件夹
            if (!delete) {
                throw new Exception("删除文件夹失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 删除文件
    public static boolean delAllFile(String path) {
        boolean flag = false;
        File file = new File(path);
        if (!file.exists()) {
            return flag;
        }
        if (!file.isDirectory()) {
            return flag;
        }
        String[] tempList = file.list();
        File temp = null;
        for (int i = 0; i < tempList.length; i++) {
            if (path.endsWith(File.separator)) {
                temp = new File(path + tempList[i]);
            } else {
                temp = new File(path + File.separator + tempList[i]);
            }
            if (temp.isFile()) {
                boolean delete = temp.delete();
                if (!delete) {
                    log.error("删除文件夹失败");
                }
            }
            if (temp.isDirectory()) {
                delAllFile(path + "/" + tempList[i]);//先删除文件夹里面的文件
                delFolder(path + "/" + tempList[i]);//再删除空文件夹
                flag = true;
            }
        }
        return flag;
    }
}
